
#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__
#include <iostream>
#include <string>
#include <memory>
#include<functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<strings.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "Mutex.hpp"
#include<cstring>
#include<cerrno>
#include"Common.hpp"
#include"InetAddr.hpp"
#include "ThreadPool.hpp"
using namespace std;
using namespace LogModule;
using namespace LockModule;
const static int gsockfd = -1;//

using adduser_t = std::function<void(InetAddr& id)>;
using task_t = std::function<void()>;
using remove_t = std::function<void( InetAddr& id)>;
using route_t = std::function<void(int sockfd,const string& message)>;

using namespace ThreadPoolModule;
//const static string gdefaultip = "127.0.0.1" ;//默认ip 本地主机
const static uint16_t gdefaultport = 8080; 
//默认端口号 开发人员常用 8080 作为测试服务器的端口，因为它是非特权端口


class nocopy
{
public:
 nocopy(){}
 nocopy(const nocopy &) = delete;
 const nocopy& operator = (const nocopy &) = delete;
 ~nocopy(){}
};

class UdpServer : public nocopy
{

public:
    UdpServer(uint16_t port = gdefaultport) 
    : _sockfd(gsockfd),
    _addr(port),
    _isrunning(false)
    {

    }
    ~UdpServer() 
    {
        if(_sockfd > gsockfd)
         ::close(_sockfd);
    }

    //下面这份代码都是套路
    void InitServer()
    {
        // 1.创建socket
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); //告诉操作系统要创建一个网络套接字
        if (_sockfd < 0) // 创建套接字失败
        {
            LOG(LogLevel::FATAL) << "socket: " << strerror(errno); // 输出日志信息
            Die(1);   // 服务直接死掉设置退出码
        }
        LOG(LogLevel::INFO) << "socket success, sockfd is :" << _sockfd <<"\n";
        //2. 填充网络信息并进行bind绑定
        // struct sockaddr_in local; 
        // bzero(&local,sizeof(local));//这个结构会有填充字段为了追求local本身确定性建议先清0再填充
        // local.sin_family = AF_INET;//用来填充addr结构填充网络信息 套接字结构和local结构的类型一样才能绑定
        // local.sin_port = htons(_port); //?? 要被发送给对方的,即发送到网络中
        // //local.sin_addr.s_addr = ::inet_addr(_ip.c_str());
        // local.sin_addr.s_addr =  INADDR_ANY;

        //2.1 绑定
        int n = ::bind(_sockfd,_addr.Netaddr(),_addr.NetAddrLen());
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind: " << strerror(errno) << "\n";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success\n";
    }
   
   void RegisterService(adduser_t adduser,route_t route,remove_t removeuser)
   {
      _adduser = adduser;
      _router = route;
      _remove = removeuser;
   }
    
    void Start() 
    {
       _isrunning = true;
       while(true)
       {
         char inbuffer[1024]; //string
         struct sockaddr_in peer;
         socklen_t len = sizeof(peer);
         ssize_t n = ::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len); //-1预留位置当字符串存\0
         if(n > 0) //读取消息成功：1.要知道消息内容 2.谁发的
         {
             InetAddr cli(peer);
             inbuffer[n] = 0;
             string message;
            if(strcmp(inbuffer,"QUIT") == 0)
            {
                //移除观察者
                _remove(cli);
                message = cli.Addr() + "# " + "我走了你们聊吧..";
            } 
            else
            {
                   //回调-> 2.新增用户
                  _adduser(cli); 
                   message = cli.Addr() + "#" + inbuffer;
            }

           

          

            //3. 构建转发任务,推送给线程池,让线程池进行消息转发!
            task_t task = bind(UdpServer::_router,_sockfd,message);
            ThreadPool<task_t>::getInstance()->Enqueue(task);


            //  uint16_t clientport = ::ntohs(peer.sin_port);//客户端的端口
            //  string clientip = ::inet_ntoa(peer.sin_addr); //客户端的ip 这是网络序列我们需要先转化！


            //  string clientinfo = cli.Ip() + ":" + to_string(cli.Port()) +" # " + inbuffer;

            //  LOG(LogLevel::DEBUG) << clientinfo;
            //  string echo_string = "echo# " ; //表示回显的消息
            //  echo_string += inbuffer;
            //  ::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));
         }

       } 
        _isrunning = false;
    }

private:
    int _sockfd;
    InetAddr _addr;
//     uint16_t _port; //服务器未来的端口号
//    // string _ip;     //服务器所对应的IP
    bool _isrunning; //服务器运行状态
    //新增用户
    adduser_t _adduser;
    //移除用户
    remove_t _remove;
    //数据转发
    route_t _router;
};

#endif